const isFunction = (val) => typeof val === 'function';
const isString = (val) => typeof val === 'string';
const isPlainObject = (val)=>typeof val === 'object' && Object.getPrototypeOf(val)===Object.prototype;
var Vuex = {
    Store: function ({ state,getters,mutations,actions,modules }) {
        //先把传入的状态变成响应式对象然后赋值给this.state 
        this.state = Vue.observable(state);
        //初始化子模块对象
        this.modules = modules||{};
        this.mutations=mutations||{};
        //遍历子模块对象
        for(const moduleName in this.modules){//moduleName=counter
            const module = this.modules[moduleName];//{state:()=>({count:1})}
            //this.state.counter1=
            //先获取 此模块的状态
            const state = isFunction(module.state)?module.state():module.state;
            //把此子模块的状态合并到this.state上 this.state.counter1={count:1}
            this.state[moduleName]=Vue.observable(state);
        }

        //初始化actions对象
        this.actions = actions||{};
        //定义一个派发动作的方法，可以派发type类型的动作
        this.dispatch = function(type,payload){
            if(isPlainObject(type)){
                payload=type;//{type:'increment',amount:5}
                type=type.type;//increment
            }
            //通过动作类型找到对应动作函数，执行
            let result =  this.actions[type]?.(this,payload);
            //让模块内的actions进行响应
            for(let moduleName in this.modules){
                //获取每个模块的实例
                let module = this.modules[moduleName];
                //如果模块支持命名空间的话
                if(module.namespaced){
                    const commit = (type,payload,options)=>this.commit(
                        `${moduleName}/${type}`,payload,options);
                    const context = {
                        ...this,
                        state:this.state[moduleName],//state是局部状态
                        rootState:this.state,//根状态
                        commit
                    };
                    const parts = type.split('/');//counter1/increment [counter1,increment]
                    if(parts[0] === moduleName){
                        //如果启动命名空间的话，action函数中获取到的commit是局部化后的commit 
                        let action = module.actions[parts[1]]
                        if(typeof action === 'function')
                            action?.(context,payload);
                    }
                    //通过派发的动作类型去对象里找一下有没有对应的配置对象 action
                    let action = module.actions[type];
                    if(action){
                        if(isPlainObject(action) && action.root==true){
                            action.handler(context,payload);
                        }
                    }

                }else{
                    //执行每个模块中的action函数
                    module.actions[type]?.({
                        ...this,
                        state:this.state[moduleName],//state是局部状态
                        rootState:this.state//根状态
                    },payload);
                }
               
            }
            return result;
        }
        //给仓库实例添加一个commit方法，用于提交变更事件,参数是事件的类型
        this.commit = (type,payload,options={root:false})=>{
            //type={type:'increment',amount:5}
            if(isPlainObject(type)){
                payload=type;//{type:'increment',amount:5}
                type=type.type;//increment
            }
            //如果root为true,表示向全局派发事件
            if(options&&options.root){
                //counter1/add => [counter1,add] => add
                type=type.split('/').pop();
            }
            //根据事件类型在mutations对象中找到对应的函数然后执行
            //你可以向 store.commit 传入额外的参数，即 mutation 的 载荷（payload）
            this.mutations[type]?.(this.state,payload);
            //调用每个模块内部的mutations
            for(let moduleName in this.modules){
                //获取模块的实例
                let module = this.modules[moduleName];
                //type add counter1/add 只有commit的type有命名空间前缀，而且命名空间前缀和模块名一样
                if(module.namespaced){//如果此模块是支持命名空间的
                    const parts = type.split('/');//counter1/add [counter,add]
                    if(parts[0] === moduleName){
                        module.mutations[parts[1]]?.(this.state[moduleName],payload,this.state);
                    }
                }else{
                    module.mutations[type]?.(this.state[moduleName],payload,this.state);
                }
            }
        }
        //初始化仓库自己的getters
        this.getters = {};
        //如果用户传过来了getters
        if(getters){
            //遍历用户传过来的getters
            for(const [key,value] of Object.entries(getters)){
                // 给仓库自己的getters上定义属性，值是一个getter函数
                //函数执行的时候会传递仓库状态和仓库的getters对象
                Object.defineProperty(this.getters,key,{
                    get:()=>{
                        return value(this.state,this.getters);
                    }
                });
            }
        }
        //把模块内的getters全部合并到根getters
        for(let moduleName in this.modules){
            //获取每个模块的实例
            let module = this.modules[moduleName];
            //遍历模块内的getters
            for(let getterKey in module.getters){
                //给根getters定义属性
                let moduleGetterKey  = getterKey;
                 //如果此模块支持命名空间
                if(module.namespaced){
                    //给getter的key添加命名空间的前缀  counter1/doubleCount1
                    moduleGetterKey=`${moduleName}/${getterKey}`
                }
                Object.defineProperty(this.getters,moduleGetterKey,{
                    get:()=>{
                        return module.getters[getterKey](
                           this.state[moduleName],//第1个参数传递模块内部的本地状态
                           module.getters,
                           this.state,//根状态，也就是rootState
                           this.getters,//根getter
                        );
                    }
                });
            }
        }
    },
    install(Vue) {
        //给全局的Vue添加一个混入选项
        Vue.mixin({
            //添加一个创建前的钩子函数
            beforeCreate() {
                console.log(this);
                //把传递给选项中的store属性赋值给Vue实例的$store
                //这是一全局的混入，所以不管是根实例，还是子组件都会有this.$store
                if (this.$options.store) {
                    this.$store = this.$options.store;
                } else if (this.$parent) {
                    this.$store = this.$parent.$store;
                }
            }
        })
    },
    //你可以将模块的空间名称字符串作为第一个参数传递给上述函数
    mapState(namespace,map) {
        if(arguments.length===1){
            map=namespace;
            namespace='';
        }
        //声明一个计算属性对象
        let computed = {};
        if (Array.isArray(map)) {
            map.forEach(key=>{
                //定义计算属性，取值去状态对象上取对应的属性
                computed[key] = function () {
                    if(namespace){
                        return this.$store.state[namespace][key];
                    }else{
                        return this.$store.state[key];
                    }
                 
                }
            });
        } else {
            //遍历映射对象
            for (const [key, value] of Object.entries(map)) {
                //key=counter value=state=>state.count
                //判断 value是不是函数
                if (isFunction(value)) {
                    //给computed赋值，属性就是count，值是一个计算值的函数
                    computed[key] = function () {
                        //执行value方法，传入仓库的状态 
                        //this指向Vue实例，$store指向仓库 this.$store.state 指向仓库的状态
                        if(namespace){
                            return value.call(this, this.$store.state[namespace]);
                        }else{
                            return value.call(this, this.$store.state);
                        }
                    }
                    //如果值是一个字符串的话,表示想要获取仓库状态上的value对应的属性
                } else if (isString(value)) {
                    //computed.countAlias this.$store.state.count 0
                    computed[key] = function () {
                        if(namespace){
                            return this.$store.state[namespace][key];
                        }else{
                            return this.$store.state[key];
                        }
                    }
                }
            }
        }
        return computed;
    },
    mapGetters(namespace,getterNames){
        if(arguments.length===1){
            map=namespace;
            namespace='';
        }
        //先声明空的计算属性对象
        let computed = {};
        //遍历要映射的getter的名称数组
        getterNames.forEach(getterName=>{
            //添加新的计算属性，属性名叫 doneTodos
            //值是一个计算函数，在访问属性的时候会执行计算函数获取返回值
            //访问仓库的getters上的属性来获取 对应的值
            computed[getterName]=function(){
                if(namespace){
                    return this.$store.getters[namespace+"/"+getterName];
                }else{
                    return this.$store.getters[getterName];
                }
               
            }
        });
        return computed;
    },
    mapMutations(namespace,mutationNames){
        if(arguments.length===1){
            map=namespace;
            namespace='';
        }
        const methods = {};
        mutationNames.forEach(mutationName=>{
            methods[mutationName]=function(payload){
                //向仓库提交一个变更，变更的名字就是decrement
                if(namespace){
                    this.$store.commit(namespace+"/"+mutationName,payload);
                }else{
                    this.$store.commit(mutationName,payload);
                }
                
            }
        });
        return methods;
    },
    mapActions(namespace,actionNames){
        if(arguments.length===1){
            map=namespace;
            namespace='';
        }
        const methods = {};
        actionNames.forEach(actionName=>{
            methods[actionName] = function(payload){
                if(namespace){
                    this.$store.dispatch(namespace+"/"+actionName,payload);
                }else{
                    this.$store.dispatch(actionName,payload);
                }
               
            }
        });
        return methods;
    },
    createNamespacedHelpers(namespace){
        return {
            mapState:(map)=>this.mapState(namespace,map),
            mapGetters:(map)=>this.this.mapGetters(namespace,map),
            mapMutations:(map)=>this.this.mapMutations(namespace,map),
            mapActions:(map)=>this.this.mapActions(namespace,map)
        }
    }
}